Skip to content

Java 四种类型的引用

根据垃圾收集器对引用的行为,有四种类型的 Java 引用。

  • 强引用 - 这是 Java 中的默认引用。当创建一个对象时,强引用将被创建。
  • 弱引用 - 弱引用可以通过使用 lang.ref.WeakReference 类来创建。
  • 软引用 - 软引用可以通过使用 lang.ref.SoftReference 类来创建。
  • 虚引用 - 虚引用可以通过使用 lang.ref.PhantomReference 类来创建。

强引用

我们通常在编写 Java 代码创建对象时使用强引用。具有强引用且在内存中处于活动状态的对象不符合垃圾回收的条件,强引用指向 null 的对象可以被垃圾回收。

java
package study.helloworld.hellojava.ref;

/**
 * 强引用 - 这是 Java 中的默认引用。当创建一个对象时,强引用将被创建。
 *
 * 具有强引用且在内存中处于活动状态的对象不符合垃圾回收的条件,强引用指向 null 的对象可以被垃圾回收。
 */
public class StrongReferenceSample {

	public static void main(String[] args) {
		Object object = new Object();
		System.out.println("object=" + object);

		object = null;
		System.out.println("object=" + object);
	}

}
sh
$ java StrongReferenceSample.java
object=java.lang.Object@130f889
object=null

弱引用

弱引用适合进行垃圾回收。一旦 JVM 检测到带有弱引用的对象,这个对象就会被标记,并且当 JVM 运行垃圾收集器线程时,垃圾就会被收集。弱引用可以通过类 lang.ref.WeakReference 创建。

java
package study.helloworld.hellojava.ref;

import java.lang.ref.WeakReference;

/**
 * 弱引用 - 弱引用可以通过使用 lang.ref.WeakReference 类来创建。
 *
 * 一旦 JVM 检测到带有弱引用的对象,这个对象就会被标记,并且当 JVM 运行垃圾收集器线程时,垃圾就会被收集。
 */
public class WeakReferenceSample {

	public static void main(String[] args) {
		WeakReference<Object> weakReference = new WeakReference<>(new Object());

		System.out.println("object=" + weakReference.get());

		System.gc();
        System.runFinalization();

		System.out.println("object=" + weakReference.get());

	}

}
sh
$ java WeakReferenceSample.java
object=java.lang.Object@221af3c0
object=null

软引用

直到 JVM 耗尽内存或 JVM 迫切需要内存时,软引用对象才符合垃圾收集的条件。软引用可以通过类 lang.ref.SoftReference 创建。

java
package study.helloworld.hellojava.ref;

import java.lang.ref.SoftReference;

/**
 * 软引用 - 软引用可以通过使用 lang.ref.SoftReference 类来创建。
 *
 * 直到 JVM 耗尽内存或 JVM 迫切需要内存时,软引用对象才符合垃圾收集的条件。
 */
public class SoftReferenceSample {

	public static void main(String[] args) {
		SoftReference<SoftObject> softReference = new SoftReference<>(
				new SoftObject());

		System.out.println("object=" + softReference.get());

		@SuppressWarnings("unused")
		byte[] data = new byte[1024 * 1024];

		System.out.println("object=" + softReference.get());

	}

	private static class SoftObject {

		@SuppressWarnings("unused")
		private byte[] data = new byte[1024 * 1024];

	}

}
sh
$ java -Xms5M -Xmx5M SoftReferenceSample.java
object=study.helloworld.hellojava.ref.SoftReferenceSample$SoftObject@351d0846
object=null

软引用的应用场景(缓存)

java
package study.helloworld.java.ref.cache;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class MyCache<K, V> {

	private Map<K, SoftReference<V>> map = new ConcurrentHashMap<>();

	private ReferenceQueue<V> referenceQueue = new ReferenceQueue<>();

	@SuppressWarnings("unchecked")
	public void put(K k, V v) {
		MySoftReference reference;
		while ((reference = (MyCache<K, V>.MySoftReference) referenceQueue
				.poll()) != null) {
			map.remove(reference.k);
		}

		map.put(k, new MySoftReference(k, v, referenceQueue));
	}

	public V get(K k) {
		SoftReference<V> softReference = map.get(k);
		if (softReference == null) {
			return null;
		}

		V v = softReference.get();
		if (v == null) {
			map.remove(k);
			return null;
		}
		return v;
	}

	public int size() {
		return map.size();
	}

	private class MySoftReference extends SoftReference<V> {

		private K k;

		public MySoftReference(K k, V referent, ReferenceQueue<? super V> q) {
			super(referent, q);
			this.k = k;
		}

	}

	public static void main(String[] args) {
		MyCache<Integer, Byte[]> myCache = new MyCache<>();
		int num = 1;
		while (true) {
			Byte[] data = new Byte[1024 * 1024];
			System.out.println(
					String.format("cache put num=%s, data=%s", num, data));
			myCache.put(num, data);
			num++;

			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			System.out.println(String.format("cache size=%s", myCache.size()));
		}

	}

}
sh
$ java -Xms9M -Xmx9M MyCache.java
cache put num=1, data=[Ljava.lang.Byte;@7f552bd3
cache size=1
cache put num=2, data=[Ljava.lang.Byte;@460d0a57
cache size=1
cache put num=3, data=[Ljava.lang.Byte;@47d90b9e
cache size=1
cache put num=4, data=[Ljava.lang.Byte;@1184ab05
cache size=1
cache put num=5, data=[Ljava.lang.Byte;@3aefe5e5
cache size=1
cache put num=6, data=[Ljava.lang.Byte;@149e0f5d
...

虚引用

虚引用的对象可用于垃圾收集,但在垃圾收集之前,一个对象会被放在一个引用队列中。虚引用可以通过类 lang.ref.PhantomReference 创建。

java
package study.helloworld.hellojava.ref;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 虚引用 - 虚引用可以通过使用 lang.ref.PhantomReference 类来创建。
 * 
 * 虚引用的对象可用于垃圾收集,但在垃圾收集之前,一个对象会被放在一个引用队列中。
 */
public class PhantomReferenceSample {

	public static void main(String[] args) {
		ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
		PhantomReference<Object> phantomReference = new PhantomReference<>(
				new Object(), referenceQueue);

		System.out.println("phantomReference=" + phantomReference);
		System.out.println("object=" + phantomReference.get());
		System.out.println("reference=" + referenceQueue.poll());

		System.gc();
		System.runFinalization();

		System.out.println("phantomReference=" + phantomReference);
		System.out.println("object=" + phantomReference.get());
		System.out.println("reference=" + referenceQueue.poll());
	}

}
sh
$ java PhantomReferenceSample.java
phantomReference=java.lang.ref.PhantomReference@29ba4338
object=null
reference=null
phantomReference=java.lang.ref.PhantomReference@29ba4338
object=null
reference=java.lang.ref.PhantomReference@29ba4338

Released under the MIT License.